#ifndef DATA_HELPER_HPP
#define DATA_HELPER_HPP

#include "config.h"

#include <concepts>
#include <json/json.hpp>
#include <utils/path_utils.hpp>
#include <utils/singleton.hpp>

struct DIARY_APP_SDK_API LoadSaveFile {
    using Json = nlohmann::json;

    bool LoadFile (const char *file_name, Json &j);

    void SaveFile (const char *file_name, const Json &j);
};

template <typename DataType>
    requires std::copyable<DataType>
class JsonPersistenceData final : public PublicSingleton<JsonPersistenceData<DataType>>, LoadSaveFile
{
public:
    using SaveFunc = void (*) (const DataType &data, Json &doc);
    using LoadFunc = void (*) (DataType &data, const Json &doc);

    inline DataType &Data ()
    {
        return data_;
    }

    void Save (DataType data)
    {
        data_ = std::move (data);
        SaveToFile ();
    }

private:
    friend PublicSingleton<JsonPersistenceData<DataType>>;
    JsonPersistenceData ()
    {
        LoadFromFile ();
    }
    ~JsonPersistenceData ()
    {
        SaveToFile ();
    }
    void LoadFromFile ()
    {
        Json j;
        if (LoadFile (s_file_name, j))
            s_load (data_, j);
    }
    void SaveToFile ()
    {
        Json j;
        s_save (data_, j);
        SaveFile (s_file_name, j);
    }

    DataType           data_ {};
    static const char *s_file_name;
    static SaveFunc    s_save;
    static LoadFunc    s_load;
};

#define DECLARE_DATA_STORE(Type) using Type##Store = JsonPersistenceData<Type>

#define IMPL_DATA_STORE(Type, FileName, Load, Save)   \
    template <>                                       \
    const char *Type##Store::s_file_name = FileName;  \
    template <>                                       \
    Type##Store::LoadFunc Type##Store::s_load = Load; \
    template <>                                       \
    Type##Store::SaveFunc Type##Store::s_save = Save

template <typename T>
struct DefaultJsonSer {
    using Json = nlohmann::json;
    static void Save (const T &t, Json &j)
    {
        j = t;
    }
    static void Load (T &t, const Json &j)
    {
        j.get_to (t);
    }
};

#define IMPL_DATA_STORE_DEFAULT(Type, FileName) IMPL_DATA_STORE (Type, FileName, &DefaultJsonSer<Type>::Load, &DefaultJsonSer<Type>::Save)

/**
 * in header:
 * struct SomeType {...};
 * DECLARE_DATA_STORE(SomeType)
 *
 * in source
 * IMPL_DATA_STORE(SomeType,"some_file.json",&SomeTypeLoad,&SomeTypeSave);
 * or
 * IMPL_DATA_STORE_DEFAULT(SomeType,"some_file.json");
 */

#define StoreLoad(T) JsonPersistenceData<T>::Instance().Data()
#define StoreSave(V) JsonPersistenceData<std::decay_t<decltype(V)>>::Instance().Save(V)

#endif /* DATA_HELPER_HPP */
